using System;
using System.Linq.Expressions;
using System.Net.NetworkInformation;
using System.Threading;
using HslCommunication;
using HslCommunication.Profinet.Siemens;
using Catastrophe.AutoSulution.EDC;

namespace HslCommunicationEx.HslNet
{
	public abstract class BaseSieCpuNdo
	{
		private SiemensS7NetEx _sieS7Net;

		public NetWorkDeviceOpr NetWorkDeviceOpr;

		private int _location1Value = 0;

		public SiemensPLCS? SiePlcModel { get; set; }

		public EndianModeEnum? EndianMode { get; set; }

		public SiemensS7NetEx SieS7Net => this._sieS7Net;

		public string IpAdr { get; set; } = "";


		public int ConTimeOut { get; set; } = 1000;


		public string Name { get; set; }

		public bool ConState => this._sieS7Net?.IsConnected ?? false;

		public bool IsConnected => this._sieS7Net?.IsConnected ?? false;

		public ushort AttempSendTimes { get; set; } = 2;


		public ushort AttempInterval { get; set; } = 30;


		public bool IsPing { get; set; }

		private OperateResult BaseIni(SiemensPLCS sieCpu, EndianModeEnum endian, byte? slot = null, byte? rack = null, int? port = null)
		{
			try
			{
				if (this._sieS7Net == null)
				{
					this._sieS7Net = new SiemensS7NetEx(sieCpu)
					{
						IpAddress = this.IpAdr,
						ConnectTimeOut = this.ConTimeOut
					};
					if (slot.HasValue)
					{
						this._sieS7Net.Slot = slot.Value;
					}
					if (rack.HasValue)
					{
						this._sieS7Net.Rack = rack.Value;
					}
					if (port.HasValue)
					{
						this._sieS7Net.Port = port.Value;
					}
					this.NetWorkDeviceOpr = new NetWorkDeviceOpr(this._sieS7Net)
					{
						EndianMode = endian
					};
				}
				if (!this._sieS7Net.IsConnected)
				{
					if (this.Ping(this.IpAdr, this.ConTimeOut))
					{
						return this._sieS7Net.ConnectServer();
					}
					return new OperateResult
					{
						Message = "ping failed!"
					};
				}
				return new OperateResult
				{
					IsSuccess = true
				};
			}
			catch (Exception ex)
			{
				throw ex;
			}
		}

		protected void RequestData(Action connectAct, Action commAct, int waitTime = 3000)
		{
			if (connectAct == null)
			{
				throw new ArgumentNullException("connectAct");
			}
			if (!this.IsConnected)
			{
				connectAct();
				if (!this.IsConnected)
				{
					Thread.Sleep(waitTime);
				}
				else
				{
					commAct?.Invoke();
				}
			}
			else
			{
				commAct?.Invoke();
			}
		}

		private OperateResult BaseIni<T>(SiemensPLCS sieCpu, EndianModeEnum endian, byte? slot = null, byte? rack = null, int? port = null) where T : class, new()
		{
			OperateResult result = this.BaseIni(sieCpu, endian, slot, rack, port);
			this.NetWorkDeviceOpr.CreateDsSchema<T>();
			return result;
		}

		[Obsolete("manual start,miss function VarMonitorIni,use Start<T> instead!")]
		public OperateResult Start(SiemensPLCS sieCpu, EndianModeEnum endian, byte? slot = null, byte? rack = null, int? port = null)
		{
			this.SiePlcModel = sieCpu;
			this.EndianMode = endian;
			OperateResult operateResult = this.BaseIni(sieCpu, endian, slot, rack, port);
			if (operateResult.IsSuccess)
			{
				this.RunAfterConnect();
			}
			return operateResult;
		}

		public OperateResult Start<T>(SiemensPLCS sieCpu, EndianModeEnum endian, byte? slot = null, byte? rack = null, int? port = null) where T : class, new()
		{
			this.SiePlcModel = sieCpu;
			this.EndianMode = endian;
			OperateResult operateResult = this.BaseIni<T>(sieCpu, endian, slot, rack, port);
			if (operateResult.IsSuccess)
			{
				this.RunAfterConnect();
			}
			return operateResult;
		}

		public virtual void Dispose()
		{
			this._sieS7Net?.Dispose();
		}

		public virtual OperateResult WriteSingle<T>(Expression<Func<T, object>> exp, dynamic value) where T : class, new()
		{
			return this.NetWorkDeviceOpr.WriteEntitySingle<T>(exp, value, this.AttempSendTimes, this.AttempInterval);
		}

		public virtual OperateResult WriteSingle(Type entityType, string fileName, dynamic value)
		{
			return this.NetWorkDeviceOpr.WriteEntitySingle(entityType, fileName, value, this.AttempSendTimes, this.AttempInterval);
		}

		public virtual Tuple<T, OperateResult> ReadEntityMuti<T>(Expression<Func<T, object>> exp) where T : class, new()
		{
			return this.NetWorkDeviceOpr.ReadEntityMuti(exp, isSerialRead: true);
		}

		public virtual Tuple<T, OperateResult> ReadEntityMuti<T>(T dstT) where T : class, new()
		{
			return this.NetWorkDeviceOpr.ReadEntityMuti(dstT);
		}

		public virtual OperateResult WriteSingle<T>(string filedName, dynamic value) where T : class, new()
		{
			return this.NetWorkDeviceOpr.WriteEntitySingle<T>(filedName, value, this.AttempSendTimes, this.AttempInterval);
		}

		public virtual OperateResult WriteMultiple<T>(T t, Expression<Func<T, object>> exp) where T : class, new()
		{
			return this.NetWorkDeviceOpr.WriteEntityMuti(t, exp, this.AttempSendTimes, this.AttempInterval);
		}

		private void RunAfterConnect()
		{
			if (Interlocked.Exchange(ref this._location1Value, 1) == 0)
			{
				this.ExcuteOnce();
			}
		}

		public virtual void ExcuteOnce()
		{
		}

		public abstract OperateResult Initialize();

		public bool Ping(string ip, int timeout = 3000)
		{
			if (!this.IsPing)
			{
				return true;
			}
			Ping ping = new Ping();
			try
			{
				PingReply pingReply = ping.Send(ip, timeout);
				if (pingReply != null && pingReply.Status == IPStatus.Success)
				{
					return true;
				}
				return false;
			}
			catch (Exception)
			{
				return false;
			}
		}
	}
}
